﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Commands
{
    /// <summary>
    /// Class, representing command, which must be executed.
    /// </summary>
    public class Command
    {
        public Command(string name)
        {
            Name = name;
            Succeeded = false;
        }

        public Command(string name, string defaultParameterValue)
        {
            Name = name;
            var parameters = new Parameters();
            parameters.DefaultParameter = defaultParameterValue;
            Parameters = parameters;
        }

        #region public properties

        public string Name
        {
            get;
            private set;
        }

        public Parameters Parameters
        {
            get
            {
                if (_param == null)
                    _param = new Parameters();
                return _param;
            }

            set { _param = value; }
        }

        public bool Succeeded
        {
            get;
            internal set;
        }

        public Exception Exception
        {
            get;
            set;
        }

        #endregion

        #region private fields

        Parameters _param;

        #endregion

        /// <summary>
        /// Address, frim which command has been received.
        /// </summary>
        public string SenderAddress { get; set; }

        /// <summary>
        /// Folder where attached files has been saved.
        /// </summary>
        public string FolderWithSavedAttachesPath { get; set; }
    }

    /// <summary>
    /// List of command parameters.
    /// </summary>
    public class Parameters : IEnumerable<Parameter>
    {
        public string DefaultParameter { get; set; }

        /// <summary>
        /// Try to find parameter with any of provided names.
        /// </summary>
        /// <param name="names">Names for parameter to seek.</param>
        /// <returns>First exist parameter value, or null if no parameter exist.</returns>
        public string GetParameter(params string[] names)
        {
            foreach (var name in names)
            {
                var param = this.FirstOrDefault(x => x.Name == name.ToLower());

                if (param != null)
                    return param.Value;
            }

            return null;
        }

        /// <summary>
        /// Try to find parameter with any of provided names.
        /// </summary>
        /// <param name="names">Names for parameter to seek.</param>
        /// <returns>First exist parameter value, or null if no parameter exist.</returns>
        public string GetParameter(IEnumerable<string> names)
        {
            return GetParameter((string[])names);
        }

        /// <summary>
        /// Check that there is parameter with any of the names in parameters list.
        /// </summary>
        /// <param name="names">Names to check.</param>
        internal bool ParameterExist(params string[] names)
        {
            return GetParameter(names) != null;
        }

        /// <summary>
        /// Get mail address from parameters. Returns parameter with 'm', "mail" name, if it is null - 
        /// return 'sender' parameter.
        /// </summary>
        /// <exception cref="parameters">If no mail parameter.</exception>
        public string GetMailAddress()
        {
            return GetParameter(MailParameterNames);
        }

        /// <summary>
        /// Return first value from parameters: "d", "dir", "directory", 
        /// "f", "folder" or null if no such parameters.
        /// </summary>
        public string GetDirectoryParameter()
        {
            return GetParameter(DirectoryParameterNames);
        }

        /// <summary>
        /// Get password from parameters "p", "pass", "password". 
        /// </summary>
        public string GetPassword()
        {
            return GetParameter(PasswordParameterNames);
        }

        public static IEnumerable<string> DirectoryParameterNames
        {
            get
            {
                return new string[] { "d", "dir", "directory", "f", "folder" };
            }
        }
        public static IEnumerable<string> MailParameterNames
        {
            get
            {
                return new string[] { "m", "mail", "mailAddress" };
            }
        }
        public static IEnumerable<string> PasswordParameterNames
        {
            get
            {
                return new string[] { "p", "pass", "password" };
            }
        }
        public static IEnumerable<string> URLParameterNames
        {
            get
            {
                return new string[] { "u", "url" };
            }
        }

        #region IEnumerable
        public IEnumerator<Parameter> GetEnumerator()
        {
           return _parameters.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _parameters.GetEnumerator();
        }

        private List<Parameter> _parameters = new List<Parameter>(); 

        #endregion

        internal void Add(string name, string value)
        {
            _parameters.Add(new Parameter(name, value));
        }

        internal void Add(string value)
        {
            _parameters.Add(new Parameter(value));
        }

        internal bool HasParametersWithoutNames()
        {
            return _parameters.Any(x => string.IsNullOrEmpty(x.Name)) && string.IsNullOrEmpty(DefaultParameter);
        }
    }

    /// <summary>
    /// Command parameter.
    /// </summary>
    public class Parameter
    {
        public Parameter(string value)
        {
            Value = value;
        }

        public Parameter(string name, string value)
        {
            Name = name.ToLower();
            Value = value;
        }

        public string Name
        {
            get;
            private set;
        }

        public string Value
        {
            get;
            set;
        }
    }
}
